package config

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"xindl/deployer/pkg/errors"
	"xindl/deployer/pkg/logger"
)

// 环境检查器
type EnvChecker struct {
	log *logger.Logger
}

// 创建环境检查器
func NewEnvChecker(log *logger.Logger) *EnvChecker {
	return &EnvChecker{log: log}
}

// 执行所有检查
func (c *EnvChecker) CheckAll(ctx context.Context) error {
	checks := []struct {
		name string
		fn   func(context.Context) error
	}{
		{"Docker安装检查", c.CheckDocker},
		{"Docker Swarm检查", c.CheckSwarm},
		{"Git安装检查", c.CheckGit},
		{"SSH配置检查", c.CheckSSH},
		{"系统资源检查", c.CheckResources},
	}

	for _, check := range checks {
		c.log.Info("执行%s...", check.name)
		if err := check.fn(ctx); err != nil {
			return errors.WithContext(err, "check", check.name)
		}
	}

	return nil
}

// 检查Docker安装
func (c *EnvChecker) CheckDocker(ctx context.Context) error {
	// 检查Docker命令
	if _, err := exec.LookPath("docker"); err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "Docker未安装").
			WithContext("error", err.Error())
	}

	// 检查Docker服务状态
	cmd := exec.CommandContext(ctx, "docker", "info")
	if err := cmd.Run(); err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "Docker服务未运行").
			WithContext("error", err.Error())
	}

	return nil
}

// 检查Docker Swarm
func (c *EnvChecker) CheckSwarm(ctx context.Context) error {
	// 检查Swarm状态
	cmd := exec.Command("docker", "info", "--format", "{{.Swarm.LocalNodeState}}")
	output, err := cmd.Output()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取Swarm状态").
			WithContext("error", err.Error())
	}

	// 如果不是active状态，尝试初始化
	if strings.TrimSpace(string(output)) != "active" {
		c.log.Info("Docker Swarm未激活，正在初始化...")

		// 获取默认网卡IP
		ip, err := c.getDefaultIP(ctx)
		if err != nil {
			c.log.Warn("无法获取默认IP地址: %v，将使用127.0.0.1", err)
			ip = "127.0.0.1"
		}

		// 初始化Swarm
		initCmd := exec.CommandContext(ctx, "docker", "swarm", "init", "--advertise-addr", ip)
		if output, err := initCmd.CombinedOutput(); err != nil {
			// 如果已经是管理节点，忽略错误
			if strings.Contains(string(output), "already part of a swarm") {
				c.log.Info("节点已经是Swarm管理节点")
				return nil
			}
			return errors.NewErrorWithCode(errors.ErrCodeSystem, "Swarm初始化失败").
				WithContext("error", string(output))
		}

		c.log.Info("Docker Swarm初始化成功")
	} else {
		c.log.Info("Docker Swarm已经处于活动状态")
	}

	return nil
}

// 获取默认网卡IP
func (c *EnvChecker) getDefaultIP(ctx context.Context) (string, error) {
	// 根据操作系统选择不同的命令
	var cmd *exec.Cmd
	switch os := runtime.GOOS; os {
	case "darwin":
		// macOS
		cmd = exec.CommandContext(ctx, "ipconfig", "getifaddr", "en0")
	case "linux":
		// Linux
		cmd = exec.CommandContext(ctx, "hostname", "-I")
	default:
		return "127.0.0.1", nil
	}

	// 执行命令获取IP
	output, err := cmd.Output()
	if err != nil {
		return "127.0.0.1", err
	}

	// 处理输出
	ip := strings.TrimSpace(string(output))
	if ip == "" {
		return "127.0.0.1", nil
	}

	// 如果是Linux，可能返回多个IP，取第一个
	if runtime.GOOS == "linux" {
		ip = strings.Fields(ip)[0]
	}

	return ip, nil
}

// 检查Git安装
func (c *EnvChecker) CheckGit(ctx context.Context) error {
	// 检查Git命令
	if _, err := exec.LookPath("git"); err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "Git未安装").
			WithContext("error", err.Error())
	}

	// 检查Git版本
	cmd := exec.CommandContext(ctx, "git", "--version")
	if err := cmd.Run(); err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "Git命令执行失败").
			WithContext("error", err.Error())
	}

	return nil
}

// 检查SSH配置
func (c *EnvChecker) CheckSSH(ctx context.Context) error {
	// 获取用户主目录
	home, err := os.UserHomeDir()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取用户主目录").
			WithContext("error", err.Error())
	}

	// 检查SSH目录权限
	sshDir := filepath.Join(home, ".ssh")
	if info, err := os.Stat(sshDir); err != nil {
		if os.IsNotExist(err) {
			return errors.NewErrorWithCode(errors.ErrCodeSystem, "SSH目录不存在").
				WithContext("dir", sshDir)
		}
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法访问SSH目录").
			WithContext("dir", sshDir).
			WithContext("error", err.Error())
	} else if info.Mode().Perm() != 0700 {
		c.log.Warn("SSH目录权限不正确，应为700")
		if err := os.Chmod(sshDir, 0700); err != nil {
			return errors.NewErrorWithCode(errors.ErrCodePermission, "无法修改SSH目录权限").
				WithContext("dir", sshDir).
				WithContext("error", err.Error())
		}
	}

	// 检查私钥文件
	privateKey := filepath.Join(sshDir, "id_rsa")
	if info, err := os.Stat(privateKey); err != nil {
		if os.IsNotExist(err) {
			return errors.NewErrorWithCode(errors.ErrCodeSystem, "SSH私钥不存在").
				WithContext("file", privateKey)
		}
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法访问SSH私钥").
			WithContext("file", privateKey).
			WithContext("error", err.Error())
	} else if info.Mode().Perm() != 0600 {
		c.log.Warn("SSH私钥权限不正确，应为600")
		if err := os.Chmod(privateKey, 0600); err != nil {
			return errors.NewErrorWithCode(errors.ErrCodePermission, "无法修改SSH私钥权限").
				WithContext("file", privateKey).
				WithContext("error", err.Error())
		}
	}

	return nil
}

// 检查系统资源
func (c *EnvChecker) CheckResources(ctx context.Context) error {
	// 根据操作系统执行不同的检查
	switch os := runtime.GOOS; os {
	case "linux":
		return c.checkLinuxResources(ctx)
	case "darwin":
		return c.checkDarwinResources(ctx)
	default:
		c.log.Warn("不支持的操作系统: %s，跳过资源检查", os)
		return nil
	}
}

// 检查Linux系统资源
func (c *EnvChecker) checkLinuxResources(ctx context.Context) error {
	// 检查内存
	cmd := exec.CommandContext(ctx, "free", "-b")
	output, err := cmd.Output()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取内存信息").
			WithContext("error", err.Error())
	}

	// 解析内存信息
	var total uint64
	fmt.Sscanf(string(output), "Mem: %d", &total)

	// 检查是否满足最小内存要求（2GB）
	if total < 2*1024*1024*1024 {
		return errors.NewErrorWithCode(errors.ErrCodeResourceLimit, "系统内存不足").
			WithContext("required", "2GB").
			WithContext("available", fmt.Sprintf("%.1fGB", float64(total)/(1024*1024*1024)))
	}

	// 检查磁盘空间
	cmd = exec.CommandContext(ctx, "df", "-B1", ".")
	output, err = cmd.Output()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取磁盘信息").
			WithContext("error", err.Error())
	}

	// 解析磁盘信息
	var available uint64
	fmt.Sscanf(string(output), "%*s %*d %*d %d", &available)

	// 检查是否满足最小磁盘空间要求（5GB）
	if available < 5*1024*1024*1024 {
		return errors.NewErrorWithCode(errors.ErrCodeResourceLimit, "磁盘空间不足").
			WithContext("required", "5GB").
			WithContext("available", fmt.Sprintf("%.1fGB", float64(available)/(1024*1024*1024)))
	}

	return nil
}

// 检查macOS系统资源
func (c *EnvChecker) checkDarwinResources(ctx context.Context) error {
	// macOS下使用不同的命令检查内存
	cmd := exec.CommandContext(ctx, "sysctl", "-n", "hw.memsize")
	output, err := cmd.Output()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取内存信息").
			WithContext("error", err.Error())
	}

	// 解析内存信息
	var total uint64
	fmt.Sscanf(string(output), "%d", &total)

	// 检查是否满足最小内存要求（2GB）
	if total < 2*1024*1024*1024 {
		return errors.NewErrorWithCode(errors.ErrCodeResourceLimit, "系统内存不足").
			WithContext("required", "2GB").
			WithContext("available", fmt.Sprintf("%.1fGB", float64(total)/(1024*1024*1024)))
	}

	// 检查磁盘空间
	cmd = exec.CommandContext(ctx, "df", "-k", ".")
	output, err = cmd.Output()
	if err != nil {
		return errors.NewErrorWithCode(errors.ErrCodeSystem, "无法获取磁盘信息").
			WithContext("error", err.Error())
	}

	// 解析磁盘信息（macOS的df输出格式略有不同）
	var availableKB uint64
	fmt.Sscanf(string(output), "%*s %*d %*d %d", &availableKB)

	// 转换为字节并检查是否满足最小磁盘空间要求（5GB）
	available := availableKB * 1024
	if available < 5*1024*1024*1024 {
		return errors.NewErrorWithCode(errors.ErrCodeResourceLimit, "磁盘空间不足").
			WithContext("required", "5GB").
			WithContext("available", fmt.Sprintf("%.1fGB", float64(available)/(1024*1024*1024)))
	}

	return nil
}
